package com.zy.core.common.security.redis;

import com.zy.core.util.asy.AsyncFactory;
import com.zy.core.util.asy.AsyncUtil;
import com.zy.core.util.other.AddressUtils;
import com.zy.core.util.servlet.ServletUtils;
import com.zy.project.system.online.entity.OnlineSession;
import com.zy.project.system.online.entity.UserOnline;
import com.zy.project.system.online.manager.UserOnlineManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class RedisSessionDAO extends EnterpriseCacheSessionDAO {

	private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
	/**
	 * shiro-redis的session对象前缀
	 */
	//@Autowired
	private RedisManager redisManager;

	@Autowired
	private UserOnlineManager userOnlineManager;
	
	/**
	 * sessionId
	 */
	private String keyPrefix = "shiro_redis_session:";

	/**
	 * 上次同步数据库的时间戳
	 */
	private static final String LAST_SYNC_DB_TIMESTAMP = RedisSessionDAO.class.getName() + "LAST_SYNC_DB_TIMESTAMP";

	/**
	 * 更新
	 * @param session
	 * @throws UnknownSessionException
	 */
	@Override
	public void doUpdate(Session session) throws UnknownSessionException {
        // 如果会话过期/停止 没必要更新
		if (session instanceof ValidatingSession && !((ValidatingSession) session).isValid()){
			return;
		}
		super.doUpdate(session);
		//其实是相当于重新保存一次session
		this.saveSession(session);
	}
	
	/**
	 * 保存
	 * @param session
	 * @throws UnknownSessionException
	 */
	private void saveSession(Session session) throws UnknownSessionException{
		if(session == null || session.getId() == null){
			logger.error("session or session id is null");
			return;
		}
		OnlineSession onlineSession = (OnlineSession)session;
		onlineSession.setSessionId((String) onlineSession.getId());
		syncToDb(onlineSession);
	}

	/**
	 * 删除session
	 * @param session
	 */
	@Override
	public void doDelete(Session session) {
		if(session == null || session.getId() == null){
			logger.error("session or session id is null");
			return;
		}
		OnlineSession onlineSession = (OnlineSession) session;
		Long delSize = redisManager.del(this.getByteKey(onlineSession.getId()));
		if (delSize > 0){
			onlineSession.setStatus(OnlineSession.OnlineStatus.off_line);
			//userOnlineManager.deleteOnlineById(String.valueOf(onlineSession.getId()));
		}

	}

	/**
	 * 获取在线的session
	 * @return
	 */
	@Override
	public Collection<Session> getActiveSessions() {
		Set<Session> sessions = new HashSet<Session>();

		Set<byte[]> keys = redisManager.keys(this.keyPrefix + "*");
		if(keys != null && keys.size()>0){
			for(byte[] key:keys){
				Session s = (Session) SerializeUtils.deserialize(redisManager.get(key));
				sessions.add(s);
			}
		}

		return sessions;
	}

	/**
	 * 创建session
	 * @param session
	 * @return
	 */
	@Override
	protected Serializable doCreate(Session session) {
		Serializable sessionId = this.generateSessionId(session);
        this.assignSessionId(session, sessionId);
        //这里调用下保存方法
        this.saveSession(session);
		return sessionId;
	}

	@Override
	protected Session doReadSession(Serializable sessionId) {
		if(sessionId == null){
			logger.error("session id is null");
			return null;
		}
		OnlineSession s = (OnlineSession) SerializeUtils.deserialize(redisManager.get(this.getByteKey(sessionId)));
		return s;
	}
	
	/**
	 * 获得byte[]型的key
	 * @param sessionId
	 * @return
	 */
	private byte[] getByteKey(Serializable sessionId){
		String preKey = this.keyPrefix + sessionId;
		return preKey.getBytes();
	}

	public RedisManager getRedisManager() {
		return redisManager;
	}

	public void setRedisManager(RedisManager redisManager) {
		this.redisManager = redisManager;
		
		/**
		 * 初始化redisManager
		 */
		this.redisManager.init();
	}

	/**
	 * Returns the Redis session keys
	 * prefix.
	 * @return The prefix
	 */
	public String getKeyPrefix() {
		return keyPrefix;
	}

	/**
	 * Sets the Redis sessions key 
	 * prefix.
	 * @param keyPrefix The prefix
	 */
	public void setKeyPrefix(String keyPrefix) {
		this.keyPrefix = keyPrefix;
	}


	/**
	 * 更新会话；如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
	 */
	public void syncToDb(OnlineSession onlineSession){
		Date lastSyncTimestamp = (Date) onlineSession.getAttribute(LAST_SYNC_DB_TIMESTAMP);
		if (lastSyncTimestamp != null){
			boolean needSync = true;
			long deltaTime = onlineSession.getLastAccessTime().getTime() - lastSyncTimestamp.getTime();
			if (deltaTime < redisManager.getExpire()){
				// 时间差不足 无需同步
				needSync = false;
			}
			boolean isGuest = onlineSession.getUserId() == null;

			// session 数据变更了 同步
			if (isGuest == false && onlineSession.isAttributeChanged()){
				needSync = true;
			}

			if (needSync == false){
				return;
			}
		}
		onlineSession.setAttribute(LAST_SYNC_DB_TIMESTAMP, onlineSession.getLastAccessTime());
		// 更新完后 重置标识
		if (onlineSession.isAttributeChanged()){
			onlineSession.resetAttributeChanged();
		}
		//AsyncUtil.me().execute(AsyncFactory.syncSessionToDb(onlineSession)); //存到数据库
		//UserOnline userOnline = initOnlinSessionData(onlineSession);
		byte[] key = getByteKey(onlineSession.getId());
		byte[] value = SerializeUtils.serialize(onlineSession);
		//存到redis中
		redisManager.set(key,value,redisManager.getExpire());
	}

	/**
	 * 初始化一些session的参数
	 * @param session
	 * @return
	 */
	private UserOnline initOnlinSessionData(OnlineSession session){
		UserOnline online = new UserOnline();
		online.setSessionId(String.valueOf(session.getId()));
		online.setDeptName(session.getDeptName());
		online.setLoginName(session.getLoginName());
		online.setStartTimestamp(session.getStartTimestamp());
		online.setLastAccessTime(session.getLastAccessTime());
		online.setExpireTime(session.getTimeout());
		online.setIpaddr(session.getHost());
		//online.setLonginLocation(AddressUtils.getRealAddressByIP(session.getHost()));
		online.setBrowser(session.getBrowser());
		online.setOs(session.getOs());
		online.setStatus(session.getStatus());
		online.setSession(session);

		return online;
	}
	
}
